package thread.test7AQS.SelfLock;

import sun.misc.Unsafe;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.LockSupport;

/**
 * 手写锁的实现
 */
public class SelfLock {
    private volatile int state=0;
    private Thread lockHolder;
    private static final Unsafe unsafe=Unsafe.getUnsafe();
    private static long stateOffset = 0;
    // 这里必须要是线程安全的，（不能用阻塞队列，因为阻塞队列本身就是线程安全的，没必要再写这一堆东西了，
    // 阻塞队列底层本身就是基于AQS的，ConcurrentLinkedQueue 的底层是依赖 CAS的）
    private final ConcurrentLinkedQueue<Thread> waiters=new ConcurrentLinkedQueue<>();

    static {
        try {
            //属性偏移量
            stateOffset=unsafe.objectFieldOffset(SelfLock.class.getDeclaredField("state"));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    public final boolean compareAndSwapState(int expect,int update){
        return unsafe.compareAndSwapInt(this,stateOffset,expect,update);
    }

    /**
     * lock 方法使用，尝试获取锁
     * @return
     */
    private boolean aquire(){
        int s=getState();
        Thread current=Thread.currentThread();
        if(s==0){//未加锁
            //因为我们这里是公平锁，所以后来者，要先判断 等待获取锁的队列里是不是还有排队的。
            //可重入锁
            if((waiters.size()==0 ||waiters.peek()==current) && compareAndSwapState(0,1)){
                setLockHolder(current);//加锁成功，当前线程持有锁
                return true;
            }
        }
        return false;
    }
    /**
     * 这里我们实现公平锁，获取锁的返回，没有获取到锁的线程，我们要保存下来。
     * @return
     */
    public void lock(){
        if(aquire()){//加锁成功
            return ;
        }
        //加锁失败的放入队列里
        ConcurrentLinkedQueue waiter=this.waiters;
        //
        Thread current=Thread.currentThread();
        waiter.add(current);

        for(;;){
            //peek 获取但不移除此队列的头
            if(waiter.peek()==current && aquire()){
                //获取到锁的元素要从队列中移除
                waiter.poll();
                return ;
            }
            //这个工具类可以让当前线程阻塞住
            LockSupport.park();
        }


    };

    public void unLock(){
        if(Thread.currentThread() !=this.lockHolder){
            throw  new RuntimeException("解锁的线程不是持有锁的线程，非法操作");
        }
        int state=getState();
        if(compareAndSwapState(1,0)){
            setLockHolder(null);
            //peek 取出，不移除,注意这个 thread 就是
            Thread current=waiters.peek();
            if(current !=null){
                //t1 释放锁，t2 唤醒
                LockSupport.unpark(current);
            }
        }
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public Thread getLockHolder() {
        return lockHolder;
    }

    public void setLockHolder(Thread lockHolder) {
        this.lockHolder = lockHolder;
    }
}
